package service

import (
	"context"
	"errors"
	"fmt"

	commonTools "algo-sdk-go/common/tools"
	"algo-sdk-go/project/matchmaking/application/command"
	"algo-sdk-go/project/matchmaking/application/dto"
	"algo-sdk-go/project/matchmaking/common/constant"
	"algo-sdk-go/project/matchmaking/domain/models/entity"
	"algo-sdk-go/project/matchmaking/domain/port/repo"
	"algo-sdk-go/project/matchmaking/domain/port/repo/ops"
)

type MatchMakingDomain struct {
	matePreferenceRepo        repo.MatePreferenceRepo
	userProfileRepo           repo.UserProfileRepo
	unverifiedUserProfileRepo repo.UnverifiedUserProfileRepo
	recommendUserRepo         repo.RecommendUserRepo
}

func NewMatchMakingDomain(matePreferenceRepo repo.MatePreferenceRepo, unverifiedUserProfileRepo repo.UnverifiedUserProfileRepo,
	userProfileRepo repo.UserProfileRepo, recommendUserRepo repo.RecommendUserRepo) *MatchMakingDomain {
	return &MatchMakingDomain{
		matePreferenceRepo:        matePreferenceRepo,
		userProfileRepo:           userProfileRepo,
		unverifiedUserProfileRepo: unverifiedUserProfileRepo,
		recommendUserRepo:         recommendUserRepo,
	}
}

// MatePreference

func (m *MatchMakingDomain) SyncMatePreference(ctx context.Context, matePreferenceCmd command.MatePreferenceCommand) (int, error) {
	userNum, err := m.matePreferenceRepo.Save(ctx, matePreferenceCmd.TransMatePreference())
	if err != nil {
		return constant.ErrorUserNum, err
	}
	return userNum, nil
}

func (m *MatchMakingDomain) LoadMatePreference(ctx context.Context, userNumList []int) ([]entity.MatePreference, error) {
	matePreferenceList, err := m.matePreferenceRepo.LoadMatePreferenceByUserNums(ctx, userNumList)
	if err != nil {
		return []entity.MatePreference{}, err
	}
	return matePreferenceList, nil
}

// UserProfile
func (m *MatchMakingDomain) SyncUserProfile(ctx context.Context, userProfileCmd command.UserProfileCommand) (int, error) {
	userNum, err := m.userProfileRepo.Save(ctx, userProfileCmd.TransUserProfile())
	if err != nil {
		return constant.ErrorUserNum, err
	}
	return userNum, nil
}

func (m *MatchMakingDomain) AppendUserProfile(ctx context.Context, userProfileCmd command.AppendUserprofileCommand) error {
	userList, err := m.userProfileRepo.LoadUserProfileByUserNums(ctx, []int{userProfileCmd.UserNum})
	if err != nil {
		return err
	}
	if len(userList) != 1 {
		return errors.New(fmt.Sprintf("user:%d not exist", userProfileCmd.UserNum))
	}

	userEntity := AppendUserProfile(userList[0], userProfileCmd)
	if err = m.userProfileRepo.Modify(ctx, userEntity); err != nil {
		return err
	}
	return nil
}

func (m *MatchMakingDomain) LoadUserProfile(ctx context.Context, condition ops.Condition) ([]entity.UserProfile, int, error) {
	userProfileList, err := m.userProfileRepo.LoadUserProfileByCondition(ctx, condition)
	if err != nil {
		return []entity.UserProfile{}, 0, err
	}

	count, err := m.userProfileRepo.CountUserProfileByCondition(ctx, condition)
	if err != nil {
		return []entity.UserProfile{}, 0, err
	}
	return userProfileList, count, nil
}

func (m *MatchMakingDomain) LoadUserAllInfo(ctx context.Context, userNumList []int) ([]dto.UserAllInfo, error) {
	var (
		userAllInfoResult = make([]dto.UserAllInfo, 0)
	)

	userProfileList, err := m.userProfileRepo.LoadUserProfileByUserNums(ctx, userNumList)
	if err != nil {
		return userAllInfoResult, err
	}

	matePreferenceList, err := m.matePreferenceRepo.LoadMatePreferenceByUserNums(ctx, userNumList)
	if err != nil {
		return userAllInfoResult, err
	}
	matePreferenceMap := commonTools.SliceToMap(matePreferenceList, func(userProfile entity.MatePreference) int { return userProfile.UserNum })

	for _, userProfile := range userProfileList {
		matePreference, exist := matePreferenceMap[userProfile.UserNum]
		if !exist {
			matePreference = entity.MatePreference{}
		}
		userAllInfoResult = append(userAllInfoResult, dto.AssembleUserAllInfo(userProfile, matePreference))
	}

	return userAllInfoResult, nil
}

// Enum
func (m *MatchMakingDomain) CommonEnumList(ctx context.Context, cmd command.EnumListCommand) ([]dto.UserAllInfo, error) {
	/*var (
		enumSceneList       = commonTools.Unique(cmd.EnumSceneList, commonTools.StringCast)
		enumSceneResultList = make([]dto.EnumScene, len(enumSceneList), len(enumSceneList))
		taskPool            = taskpool.NewTaskPool(3, 3)
	)
	defer taskPool.Close()

	for i, sceneInfo := range cmd.EnumSceneList {
		index := i
		scene := sceneInfo
		handlerFunc, exist := t.enumHandlerMap[scene]
		if !exist {
			enumSceneResultList[index] = dto.EnumScene{Key: scene}
			continue
		}

		err := taskPool.Submit(func() {
			enumList := handlerFunc(ctx, EnumParams{
				EnumKey:  scene,
				Operator: cmd.Operator,
			})
			enumSceneResultList[index] = enumList
		}, constant.EnumQueryTimeOut*time.Second)
		if err != nil {
			logger.LogErrorw("CalculateADEnum_err", "err", err, "scene", scene)
			continue
		}
	}

	return dto.AdEnumDTO{EnumSceneList: enumSceneResultList}, nil*/

	return nil, nil
}
